package logging

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
)

type Level int

const (
	DEBUG Level = iota
	INFO
	WARN
	ERROR
	FATAL
)

var ( //nolint:gochecknoglobals
	logger         *log.Logger
	logLevel       = INFO
	logAsJSON      = false
	logCaller      = false
	logTimestamp   = true
	logDestination = os.Stderr
)

// LogEntry represents a single log entry.
type LogEntry struct {
	Timestamp string `json:"timestamp,omitempty"`
	Level     string `json:"level"`
	Message   string `json:"message"`
	Caller    string `json:"caller,omitempty"`
}

// Init initializes the logger with given options.
func Init(level Level, asJSON, withCaller, withTimestamp bool) {
	logLevel = level
	logAsJSON = asJSON
	logCaller = withCaller
	logTimestamp = withTimestamp

	// Create a new logger that writes to the specified destination
	// with no prefix or flags, as we will be formatting the output ourselves.
	logger = log.New(logDestination, "", 0)
}

// Setup initializes the logger based on the verbose flag.
func Setup(verbose bool) {
	level := INFO
	if verbose {
		level = DEBUG
	}
	Init(level, false, false, true)
}

func logMessage(level Level, format string, a ...interface{}) {
	if logger == nil {
		// Fallback to default logger if not initialized
		Init(INFO, false, false, true)
	}

	if level < logLevel {
		return
	}

	levelStr := ""
	switch level {
	case INFO:
		levelStr = "INFO"
	case DEBUG:
		levelStr = "DEBUG"
	case WARN:
		levelStr = "WARN"
	case ERROR:
		levelStr = "ERROR"
	case FATAL:
		levelStr = "FATAL"
	}

	msg := fmt.Sprintf(format, a...)

	if logAsJSON {
		entry := LogEntry{
			Level:   levelStr,
			Message: msg,
		}
		if logTimestamp {
			entry.Timestamp = time.Now().Format(time.RFC3339)
		}
		// Note: Getting caller info is complex and expensive, so it's omitted for now.

		jsonEntry, err := json.Marshal(entry)
		if err != nil {
			// Fallback to plain text if JSON marshaling fails
			logger.Printf("[%s] %s\n", levelStr, msg)
			return
		}
		logger.Println(string(jsonEntry))
	} else {
		prefix := ""
		if logTimestamp {
			prefix = fmt.Sprintf("[%s] ", time.Now().Format(time.RFC3339))
		}
		logger.Printf("%s[%s] %s\n", prefix, levelStr, msg)
	}

	if level == FATAL {
		os.Exit(1)
	}
}

// Infof logs a message at INFO level.
func Infof(format string, a ...interface{}) {
	logMessage(INFO, format, a...)
}

// Debugf logs a message at DEBUG level.
func Debugf(format string, a ...interface{}) {
	logMessage(DEBUG, format, a...)
}

// Warnf logs a message at WARN level.
func Warnf(format string, a ...interface{}) {
	logMessage(WARN, format, a...)
}

// Errorf logs a message at ERROR level.
func Errorf(format string, a ...interface{}) {
	logMessage(ERROR, format, a...)
}

// Fatalf logs a message at FATAL level and exits.
func Fatalf(format string, a ...interface{}) {
	logMessage(FATAL, format, a...)
}

// Print prints a message without any level or timestamp, directly to the output.
func Print(a ...interface{}) {
	if logger == nil {
		// Fallback to default logger if not initialized
		Init(INFO, false, false, true)
	}
	// We use Fprintln to write directly to the destination, bypassing the logger's formatting.
	fmt.Fprintln(logDestination, a...)
}